#!/usr/bin/ruby

require 'timeout'
require 'thread'
load 'comm.rb'
load 'poker.rb'
load 'pt.rb'

#玩家类
class Player
	attr_accessor :cash
	attr_reader :paper_tags,:name,:w
	attr_reader :pts

	#返回@pts中所有pt的数量和
	def pts_count
		@pts.inject(0) {|s,pt_t|s + pt_t[1]}
	end

	#@pts内容为[[pt0,count],[pt1,count],[pt2,count]]
	def initialize(name,cash=100)
		@name,@pts,@cash = name,[],cash
		@r,@w = IO.pipe
	end
	
	#在真人player类里,没有也不需要读取实例变量@ctx_queue
	def set_ctx_one_trade(ctx)
		nil
	end
	
	def buy_pts(pt,count)
		if @cash <= pt.val * count
			puts("No enough money to buy #{pt} * #{count}")
			return
		end
		
		@cash -= pt.val * count
		pt_find = @pts.find {|pt_t| pt_t[0].name == pt.name}
		unless pt_find
			@pts << [pt,count]
		else
			pt_find[1] += count
		end
	end
	
	def buy_paper_tag(pt)
		buy_pts(pt,1)
	end
	
	def use_paper_tag(idx)
		raise "pt count <= 0" if @pts[idx][1] <= 0
		@pts[idx][1] -= 1
	end
  
	def str_pts
		pts = ""
		@pts.each_with_index {|pt_t,i|pts << "#{i+1}:#{pt_t[0].name}->#{pt_t[1]}\t"}
		pts
	end
  
	def to_s
		"#{@name} : 剩余现金 #{@cash} , paper_tags : \n" + str_pts + "\n"
	end

	def connect(game)
		@game_w = game.bind(self)
	end
  
	def puts_r(msg)
		@game_w.write(msg)
		#@game_w.puts(msg)
	end
  
	def gets_r
		@r.read_nonblock READ_BUF_SIZE
		#@r.gets
	rescue IO::EAGAINWaitReadable =>e
		nil
	end
  
	def r_flush
		@r.flush
	end

	def loop_gets
		count = 0
		
		until str = gets_r
			break if count >= 3
			count += 1
			sleep 0.1
		end
		#sleep 1
		str = "" unless str
		while tmp = gets_r
			str << tmp
		#player.r_flush
		end
		str = "" unless str
		str.chomp
	end

	def play_r
		loop do
			str = loop_gets
			printf str
			return if str =~ /bye/i

			until ret = stdin_gets_async
				str = loop_gets
				printf str
				return if str =~ /bye/i
			end
			puts_r ret
		end
	end
end

class PlayerAI_Frame < Player
	def initialize(cash=100)
		super(auto_name,cash)
		#线程@t是否可以开始think命令
		@can_make_cmd = false
		#think线程输出的命令队列
		@cmd_queue = SizedQueue.new(1)
		#think线程
		@t = ai_cmds_thread
		#便于检查线程中的错误,如果线程@t发生错误,则整个进程退出!
		@t.abort_on_exception = true
		#game设置的one_trade上下文队列，队列深度只有1
		@ctx_queue = SizedQueue.new(1)
		#已经完成的one_trade次数
		@trade_turn_count = 0
	end

	#ctx包括over,ing,add,last,c1,c2,rate,w_2,w_upt
	#over:是否一次交易结束(true/false)
	#ing:是否正在一次交易中(true/false)
	#add:一次交易完成后增加的cash(可正可负)
	#last:每个stock剩余的牌数组成一个数组([0,16,27,27])
	#c1,c2:交易中的c1和c2；可能为nil,表示还未翻开
	#rate:wait_double所对应的rate
	#w_2:是否正在等待输入double(true/false)
	#w_upt:是否正在等待输入use_pt(true/false)
	def set_ctx_one_trade(ctx)
		@ctx_queue.push(ctx)
	end

	def rand_name(len=6)
		chars = (('a'..'z').to_a * len).freeze
		srand
		idxes = []
		len.times {idxes << (rand*100)%chars.count}
		chars.shuffle
		chars.values_at(*idxes).join
	end
	
	def auto_name
		sprintf("AI_#{rand_name}_%04d"%(rand*10000).to_i)
	end

	def add_cmd(cmd)
		sleep 0.5
		#puts("#{__callee__}:cmd is #{cmd}")
		@cmd_queue.push(cmd)
	end

	#如果pt_name不在@pts中或pt count为0则返回nil
	#否则返回return pt_name在@pts的idx
	def can_use_pt?(pt_name)
		pt_t = @pts.find {|pt_t|pt_t[0].name == pt_name}
		return nil if pt_t == nil || pt_t[1] == 0
		@pts.index(pt_t)
	end

	#从@ctx_queue抓取一个上下文ctx，如果@ctx_queue为空则等待
	#如果带有一个block则调用，并传递ctx；否则返回ctx
	def wait_ctx_one_trade_and_call(&b)
		ctx = @ctx_queue.pop
		b.call(ctx) if block_given?
		ctx
	end

	def ai_buy_pts(pt_name,count)
		add_cmd("2\n")
		pt = PAPER_TAGS.find {|pt|pt.name == pt_name}
		idx = PAPER_TAGS.index(pt) + 1
		add_cmd("#{idx}*#{count}\n")
		add_cmd("n\n")
	end		

	def ai_buy_stock
		add_cmd("1\n")
		
		add = think_one_trade	
		
		add_cmd("n\n")
		return add
	end
	
	def ai_sh_state
		add_cmd("3\n")
		sleep(3)
	end
	
	def ai_quit_game
		add_cmd("n\n")
	end

	def ai_cmds_thread
		Thread.new do
			loop do
				if @can_make_cmd
					think_start					
				end
				sleep 1
			end
		end
	end

	def play_r
		ct = Ctxt.new
		loop do
			str = loop_gets
			printf str
			return if str =~ /bye/i
			
			@can_make_cmd = true
			
			sleep 0.5
			begin
				#puts("*"*20)
				ret = @cmd_queue.pop(true)
				#puts("#{ret}")
				ct.color("turn #@trade_turn_count:#{ret}",Ctxt::PINK)
			rescue =>e
				#err(e)
				str = loop_gets
				#puts("-"*20)
				printf str
				return if str =~ /bye/i
				sleep 0.5
				retry
			end
			
			puts_r ret
		end
	end
end

class PlayerAI_05 < PlayerAI_Frame			
	#返回第一个count不为0的stock的idx
	def think_choose_stock(counts)
		stock = counts.find {|count|count > 0}
		return nil unless stock
		counts.index(stock)
	end
	
	def think_choose_cash
		(rand*@cash).to_i + 1
	end
	
	def think_if_double(rate)
		if rate == 1
			false
		else
			true
		end
	end
			
	#可能返回nil表示不用pt,或者返回player中@pts的idx
	def think_use_pt(c1,c2,stock_count)
		#puts("#{__callee__}:#{[c1,c2,stock_count]}")
		return nil if pts_count == 0 || stock_count == 0
		if c2
			return nil if Pokers.val(c2) >= Pokers.val(c1)
			
			idx = nil
			return idx if idx = can_use_pt?("PT_ADD_5")
			return idx if idx = can_use_pt?("PT_2_AGAIN")
			return idx if idx = can_use_pt?("PT_ADD_1")
		else
			#c2没有翻开
			return idx if Pokers.val(c1) >= 11 && idx = can_use_pt?("PT_SWICH")
			return idx if Pokers.val(c1) >= 8 && idx = can_use_pt?("PT_1_AGAIN")
			return idx if idx = can_use_pt?("PT_SUB_1")
		end
		nil
	end	
	
	def think_one_trade
		thinked_stock_idx = nil
		
		#选择stock_idx和cash
		wait_ctx_one_trade_and_call do |ctx|
			thinked_stock_idx = idx = think_choose_stock(ctx[:last])
			unless idx
				#puts("所有stock都已经开完!")
				add_cmd("n\n")
				return nil
			end
			
			cash = think_choose_cash
			add_cmd("#{idx+1} #{cash}\n")
		end
		
		add = loop do
			#puts "in loop..."
			ctx = wait_ctx_one_trade_and_call
			#puts "#{__callee__}:@start : #{ctx}"
			break ctx[:add] if ctx[:over] == true && ctx[:ing] == false
			if ctx[:w_2]
				if think_if_double(ctx[:rate])
					add_cmd("y\n")
				else
					add_cmd("\n")
				end
			elsif ctx[:w_upt]
				#ret可能为nil表示不用pt或无可用的pt,否则返回@pts中的idx
				c1,c2 = ctx[:c1],ctx[:c2]
				if choosed_pt_idx = think_use_pt(c1,c2,ctx[:last][thinked_stock_idx])
					add_cmd("#{choosed_pt_idx+1}\n")
				else
					add_cmd("\n")
				end
			else
				#Do Nothing???
			end
		end
	end
		
	def think_start
		ai_buy_pts("PT_ADD_1",5)
		ai_buy_pts("PT_ADD_5",5)
		ai_buy_pts("PT_SUB_1",5)
		ai_buy_pts("PT_1_AGAIN",5)
		ai_buy_pts("PT_2_AGAIN",5)
		ai_buy_pts("PT_SWICH",5)

		while @cash > 0
			break unless ai_buy_stock
			#下一行为了摘取对应在game.rb:when_choose_menu_buy_stock中的ctx.push
			ctx = wait_ctx_one_trade_and_call
			@trade_turn_count += 1
			#puts "#{__callee__}:#{ctx}"
		end
		#ai_sh_state
		puts("bye...亲爱的#@name,最后您的现金还剩下:#@cash py")
		ai_quit_game
	end
end
